package com.jnpf.flow;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.http.Header;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpStatus;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.jnpf.api.enums.FlowApiEnum;
import jnpf.engine.model.flowtemplate.FlowPageListVO;
import jnpf.engine.model.open.*;
import jnpf.engine.model.open.resp.*;
import jnpf.util.JsonUtil;
import jnpf.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

@Component
@Slf4j
public class FlowUtil {

	public static final String JNPF_ORIGIN = "jnpf-origin";
	public static final String ENCRYPTION_KEY = "encryption-key";
	public static final String MESSAGE_ERROR_PUSH = "/api/message/SendMessageConfig/testSendConfig";


	@Value("${flow.url:defaultUrl}")
	private String flowUrl;

	@Value("${flow.timeout:6000}")
	private Integer timeout;

	@Value("${flow.key}")
	private String key;


	// 注入后的 publicKey 和 key
	private static String staticFlowUrl;
	private static Integer staticTimeout;
	private static String staticKey;
	private static final String staticPublicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDFJpt6EVTpJmipq7dGS60aI313pgnt2uC0avLNxEI8vjhgQ7CCdYBneg4zhFImlfUINfJme+gZYSJGKnKJ2AkMdmqrJ93/hzUxUY84MaZ1L7rpZhkaPIQvIvmn6pD0IpMcYJMc13n1WV+rzrR3uOplnVJ4iVgy8HM4aX/asu4eRwIDAQAB";

	@PostConstruct
	public void init() {
		staticFlowUrl = flowUrl;
		staticTimeout = timeout;
		staticKey = key;
	}

	public static void main(String[] args) {
		System.out.println(SecureUtil.rsa(null, staticPublicKey).encryptBase64("xoa" + "-" + "349057407209541", KeyType.PublicKey));
	}

	/**
	 * 简易启动流程
	 */
	public static String simpleStartFlow(FlowReq flowReq) throws FlowException {
		Assert.notNull(flowReq, "flowReq is null");
		Assert.notBlank(flowReq.getFlowCode(), "流程编码不能为空");
		Assert.notNull(flowReq.getFormData(), "流程表单参数不能为空");
		if (StringUtil.isEmpty(flowReq.getProcessInstanceId())) {
			return flowPost(FlowApiEnum.SIMPLE.getPath(), JSON.toJSONString(flowReq), flowReq.getCreateUserId(), "启动流程");
		} else {
			FlowAuditReq flowAuditReq = FlowAuditReq.builder()
					.flowCode(flowReq.getFlowCode())
					.formData(flowReq.getFormData())
					.processInstanceId(flowReq.getProcessInstanceId())
					.userId(flowReq.getCreateUserId())
					.resubmit(true)
					.build();
			return reSubmitFlow(flowAuditReq);
		}
	}

	/**
	 * 撤回流程
	 */
	public static String revokeFlow(String flowInstanceId, String reason, Long userId) throws FlowException {
		Assert.notBlank(flowInstanceId, "流程实例id不能为空");
		Map<String, Object> params = new HashMap<>();
		params.put("flowInstanceId", flowInstanceId);
		params.put("reason", reason);

		return flowPost(FlowApiEnum.REVOKE.getPath(), JSON.toJSONString(params), userId, "撤回流程");
	}

	/**
	 * 终止流程
	 */
	public static String cancelFlow(String flowInstanceId, String reason, Long userId) throws FlowException {
		Assert.notBlank(flowInstanceId, "流程实例id不能为空");
		Map<String, Object> params = new HashMap<>();
		params.put("flowInstanceId", flowInstanceId);
		params.put("reason", reason);

		return flowPost(FlowApiEnum.CANCEL.getPath(), JSON.toJSONString(params), userId, "终止流程");
	}

	/**
	 * 删除流程
	 */
	public static String deleteFlow(String flowInstanceId, Long userId) throws FlowException {
		Assert.notBlank(flowInstanceId, "流程任务id不能为空");
		Map<String, Object> params = new HashMap<>();
		params.put("instanceIds", Lists.newArrayList(flowInstanceId));
		return flowPost(FlowApiEnum.DELETE.getPath(), JSON.toJSONString(params), userId, "删除流程");
	}

	/**
	 * 终止并删除流程
	 */
	public static String cancelAndDeleteFlow(String flowInstanceId, String reason, Long userId) throws FlowException {
		String cancelResult = FlowUtil.cancelFlow(flowInstanceId, reason, userId);
		String deleteResult = FlowUtil.deleteFlow(flowInstanceId, userId);
		return deleteResult;
	}

	/**
	 * 驳回重新提交流程
	 */
	public static String reSubmitFlow(FlowAuditReq auditReq) throws FlowException {
		Assert.notBlank(auditReq.getProcessInstanceId(), "流程实例id不能为空");
		Assert.notNull(auditReq.getUserId(), "用户id不能为空");
		Assert.notNull(auditReq.getResubmit(), "是否重新提交不能为空");
		Assert.notNull(auditReq.getFormData(), "表单数据不能为空");

		return flowPost(FlowApiEnum.AUDIT.getPath(), JSON.toJSONString(auditReq), auditReq.getUserId(), "重新发起流程");
	}


	/**
	 * 代办流程
	 */
	public static FlowQueryWaitResp waitList(FlowQueryWaitReq req, Long userId) throws FlowException {
		Map<String, Object> formMap = JsonUtil.entityToMap(req);
		setFormUserId(formMap, userId);
		String dataJson = flowGet(FlowApiEnum.WAIT.getPath(), formMap, userId);
		return JsonUtil.getJsonToBean(dataJson, FlowQueryWaitResp.class);
	}

	/**
	 * 已办流程
	 */
	public static FlowQueryDoneResp doneList(FlowQueryDoneReq req, Long userId) throws FlowException {
		Map<String, Object> formMap = JsonUtil.entityToMap(req);
		setFormUserId(formMap, userId);
		String dataJson = flowGet(FlowApiEnum.DONE.getPath(), formMap, userId);
		return JsonUtil.getJsonToBean(dataJson, FlowQueryDoneResp.class);
	}

	/**
	 * 抄送事宜流程
	 */
	public static FlowQueryCopyTaskResp copyTaskList(FlowQueryCopyTaskReq req, Long userId) throws FlowException {
		Map<String, Object> formMap = JsonUtil.entityToMap(req);
		setFormUserId(formMap, userId);
		String dataJson = flowGet(FlowApiEnum.COPY.getPath(), formMap, userId);
		return JsonUtil.getJsonToBean(dataJson, FlowQueryCopyTaskResp.class);
	}

	/**
	 * 我发起的流程
	 */
	public static FlowQueryLaunchResp launchList(FlowQueryLaunchReq req, Long userId) throws FlowException {
		Map<String, Object> formMap = JsonUtil.entityToMap(req);
		setFormUserId(formMap, userId);
		String dataJson = flowGet(FlowApiEnum.LAUNCH.getPath(), formMap, userId);
		return JsonUtil.getJsonToBean(dataJson, FlowQueryLaunchResp.class);
	}

	/**
	 * 我发起且完结的流程
	 */
	public static FlowQueryMonitorResp monitorList(FlowQueryMonitorReq req, Long userId) throws FlowException {
		Map<String, Object> formMap = JsonUtil.entityToMap(req);
		setFormUserId(formMap, userId);
		String dataJson = flowGet(FlowApiEnum.MONITOR.getPath(), formMap, userId);
		return JsonUtil.getJsonToBean(dataJson, FlowQueryMonitorResp.class);
	}

	/**
	 * 流程模板列表
	 */
	public static List<FlowPageListVO> templateList(FlowTemplateListReq req, Long userId) throws FlowException {
		Map<String, Object> formMap = JsonUtil.entityToMap(req);
		setFormUserId(formMap, userId);
		String dataJson = flowGet(FlowApiEnum.TEMPLATE.getPath(), formMap, userId);
		return JsonUtil.getJsonToList(dataJson, FlowPageListVO.class);
	}


	/**
	 * 发送消息
	 */
	public static String pushMessage(MsgTemplate msgTemplate) throws FlowException {
		Assert.notNull(msgTemplate, "msgTemplate is null");
		return flowPost(MESSAGE_ERROR_PUSH, JSON.toJSONString(ImmutableList.of(msgTemplate)), null, null);
	}

	private static void setFormUserId(Map<String, Object> formMap, Long userId) {
		formMap.put("userId", userId.toString());
	}


	private static void buildParam(String field, String fieldName, String value, List<MsgTemplate.MsgTemplateParam> paramList) {
		paramList.add(MsgTemplate.MsgTemplateParam.builder()
				.field(field)
				.fieldName(fieldName)
				.value(value)
				.templateType("0")
				.templateCode("flowerror")
				.templateId("627149430981660293")
				.templateName("流程错误消息提醒")
				.build());
	}

	public static MsgTemplate buildErrorMessageTemplate(String title, String errorType, String bizType, String message, String flowInstanceId, String reqMessage) {
		List<MsgTemplate.MsgTemplateParam> paramList = new ArrayList<>();
		buildParam("@Title", "标题", title, paramList);
		buildParam("errorType", "错误类型", errorType, paramList);
		buildParam("@SendTime", "发送时间", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")), paramList);
		buildParam("flowInstanceId", "流程id", flowInstanceId, paramList);
		buildParam("bizType", "业务模块", bizType, paramList);
		buildParam("message", "错误信息", message, paramList);
		buildParam("reqMessage", errorType.contains("事件") ? "消费内容" : "请求内容", reqMessage, paramList);

		return MsgTemplate.builder()
				.id("627149637345611397")
				.msgTemplateName("流程错误消息提醒")
				.sendConfigId("627149637291085445")
				.messageType("webhook")
				.templateId("627149430981660293")
				.accountConfigId("627148679601458821")
				.paramJson(paramList)
				.build();
	}



	/**
	 * 通用 POST 请求方法
	 */
	private static String flowPost(String url, String body, Long userId, String type) throws FlowException {
		Assert.notBlank(staticKey, "标识key不能为空");
		if(Objects.nonNull(type)){
			Assert.notNull(userId, "流程启动用户不能为空");
			// 写入操作日志
			if (!FlowApiEnum.LOG.getType().equals(type)) {
				processOperatorLog(url, body, userId);
			}
		}
		HttpResponse response = null;
		try {
			String encrypt = null;
			// 加密用户标识
			if (Objects.nonNull(userId)) {
				 encrypt = SecureUtil.rsa(null, staticPublicKey).encryptBase64(staticKey + "-" + userId, KeyType.PublicKey);
			}
			// 构建并发送 POST 请求
			response = HttpRequest.post(staticFlowUrl + url)
					.header(Header.CONTENT_TYPE, "application/json;charset=UTF-8")
					.header(JNPF_ORIGIN, "pc")
					.header(ENCRYPTION_KEY, encrypt)
					.body(body)
					.timeout(staticTimeout)
					.execute();

			int statusCode = response.getStatus();
			String responseBody = response.body();

			log.info("请求 URL: {},用户ID: {},请求体: {}, 状态码: {}, 响应体: {}", staticFlowUrl + url, userId.toString(), body, statusCode, responseBody);

			// 解析响应体
			JSONObject responseJson = JSON.parseObject(responseBody);
			if (statusCode == HttpStatus.HTTP_OK && responseJson.getIntValue("code") == 200) {
				return responseJson.getString("data");
			} else {
				String message = responseJson.getString("msg");
				log.error("请求失败，状态码：{}, 响应消息：{}", statusCode, message);
				throw new FlowException("调用流程引擎请求失败: " + message);
			}

		} catch (Exception e) {
			log.error("调用流程引擎请求异常: {}", e.getMessage(), e);
			if (StringUtil.isNotEmpty(type)) {
				//增加企业微信异常推送
				pushMessage(buildErrorMessageTemplate("调用流程引擎请求异常", type, staticKey, e.getMessage(), null, body));
			}
			throw new FlowException(staticFlowUrl + "," + staticTimeout + "," + staticKey + "-------->" + "调用流程引擎请求异常", e);
		} finally {
			if (response != null) {
				response.close();
			}
		}
	}


	/**
	 * 通用 GET 请求方法
	 */
	private static String flowGet(String url, Map<String,Object> formMap, Long userId) throws FlowException {
		HttpResponse response = null;
		try {
			String encrypt = null;
			// 加密用户标识
			if (Objects.nonNull(userId)) {
				encrypt = SecureUtil.rsa(null, staticPublicKey).encryptBase64(staticKey + "-" + userId, KeyType.PublicKey);
			}
			// 构建并发送 POST 请求
			response = HttpRequest.get(staticFlowUrl + url + generatorGetParam(formMap))
					.header(JNPF_ORIGIN, "pc")
					.header(ENCRYPTION_KEY, encrypt)
					.timeout(staticTimeout)
					.execute();

			int statusCode = response.getStatus();
			String responseBody = response.body();
			log.info("请求 URL: {},用户ID: {},状态码: {}, 响应体: {}", staticFlowUrl + url, userId.toString(), statusCode, responseBody);
			// 解析响应体
			JSONObject responseJson = JSON.parseObject(responseBody);
			if (statusCode == HttpStatus.HTTP_OK && responseJson.getIntValue("code") == 200) {
				return responseJson.getString("data");
			} else {
				String message = responseJson.getString("msg");
				log.error("请求失败，状态码：{}, 响应消息：{}", statusCode, message);
				throw new FlowException("调用流程引擎请求失败: " + message);
			}

		} catch (Exception e) {
			log.error("调用流程引擎查询错误: {}", e.getMessage(), e);
			throw new FlowException(staticFlowUrl + "," + staticTimeout + "," + staticKey + "-------->" + "调用流程引擎请求异常", e);
		} finally {
			if (response != null) {
				response.close();
			}
		}
	}

	private static void processOperatorLog(String url,String body, Long userId) throws FlowException {
		Map<String,Object> logParam = new HashMap<>();
		logParam.put("serviceName",staticKey);
		logParam.put("requestArgs", body);
		logParam.put("logTime", Timestamp.valueOf(LocalDateTime.now()).getTime());
		logParam.put("logDateTime", DateUtil.format(LocalDateTime.now(), DatePattern.NORM_DATETIME_PATTERN));
		FlowApiEnum apiEnum = FlowApiEnum.getByPath(url);
		logParam.put("operatorType",apiEnum == null ? "未知" : apiEnum.getType());
		flowPost(FlowApiEnum.LOG.getPath(), JSONUtil.toJsonStr(logParam),userId,FlowApiEnum.LOG.getType());
	}

	/**
	 * 构建url 参数
	 * @param formMap
	 * @return
	 */
	private static String generatorGetParam(Map<String,Object> formMap){
		List<String> paramList = new ArrayList<>();
		formMap.forEach((k,v)->{
			String param = k + "=" + v;
			paramList.add(param);
		});
		String join = String.join("&", paramList);
		log.debug("Flow Get 请求参数 -- {}",join);
		return "?"+join;
	}


}
